package gmodel

import (
	"fmt"
	"gitee.com/851537/gbase"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/golang-module/carbon"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"reflect"
)

// IStat -
type IStat interface {
	IDbRedisHash

	//IsTotalType() bool
	SetDataId(id int64)   //impl in statBase
	GetDataId() int64     //impl in statBase
	SetStatTime(ts int64) //impl in statBase
	GetStatTime() int64   //impl in statBase

	//SetToday2Db(today2Db bool)

	IsToday2Db() bool

	GetTodayStat(child IStat) IStat //impl in statBase

	GetDbStatKeys() string                                          //具体类基类实现
	GetDbStat(child IStat, startTime, endTime int64) (IStat, error) //impl in statBase

	Addin(child IStat) error //具体类基类实现

	GetTotalModels() []IStat

	//该方法是批量把redis里的数据全部持久化到数据库，并不是针对某一条数据
	Persistent(child IStat) error
}

type BaseStat struct {
	BaseRedisLock
	BaseDbRedisHash
	Id     int64 `json:"id" gorm:"primaryKey"`
	DataId int64 `json:"data_id" gorm:"default:0"`

	//统计时间，统一为当日第一秒
	StatTime int64 `json:"stat_time"`

	UpdateTime int64 `json:"update_time"`

	//当日数据是否保存到数据库并从数据库读取
	//today2Db bool `orm:"-"`
}

func (this *BaseStat) RedisField() string {
	return fmt.Sprintf("%d-%d", this.StatTime, this.DataId)
}

// default is period type,only total stat model extend this func,and set true value
//func (this *BaseStat) IsTotalType() bool {
//	return false
//}

// 标记日统计对应的月、年、总统计模型，用于同时持久化对应模型
func (this *BaseStat) GetTotalModels() []IStat {
	return nil
}

func (this *BaseStat) GetId() int64 {
	return this.Id
}

func (this *BaseStat) SetStatTime(ts int64) {
	this.StatTime = ts
}

func (this *BaseStat) GetStatTime() int64 {
	return this.StatTime
}

func (this *BaseStat) SetDataId(id int64) {
	this.DataId = id
}

func (this *BaseStat) GetDataId() int64 {
	return this.DataId
}

// 默认保存到redis hash，子类设置为保存到数据库，实现该方法并返回true
func (this *BaseStat) IsToday2Db() bool {
	return false
}

// depend this.dataId,
func (this *BaseStat) GetTodayStat(child IStat) IStat {
	if child.IsToday2Db() {
		child.SetStatTime(gbase.NewTime().DayFirstTime())
		_ = this.LoadData(child)
	} else {
		child.SetStatTime(gbase.NewTime().DayFirstTime())
		_ = child.RedisGet(child)
	}
	return child
}

// 从数据库读取一条记录,depend this.DataId and this.statTime
func (this *BaseStat) LoadData(child IStat) error {
	wheres := NewWheres()
	wheres = append(wheres, "t.data_id", child.GetDataId())
	wheres = append(wheres, "t.stat_time", child.GetStatTime())
	return this.DbGet(child, wheres...)
}

// 从数据库读取某段日期内的统计数据之各
// depend this.dataId
func (this *BaseStat) GetDbStat(child IStat, startTime, endTime int64) (IStat, error) {
	wheres := NewWheres()
	wheres = append(wheres, "t.data_id", child.GetDataId())
	wheres = append(wheres, "t.stat_time between ? and ?", startTime, endTime)
	if err := this.DbSumsWithOpts(child, child,
		WithDbKeys(child.GetDbStatKeys()),
		WithDbWheres(wheres),
	); err != nil {
		return nil, err
	}
	return child, nil
}

func (this *BaseStat) GetStatAll(child IStat, todayStat IStat, startTime, endTime int64, wheres ...any) (IStat, error) {
	var (
		err error
		//todayStat IStat
	)
	todayFirstSecond := carbon.Now().StartOfDay().Timestamp()
	if startTime <= todayFirstSecond {
		tmpWheres := NewWheres()
		tmpWheres = append(tmpWheres, "t.data_id", child.GetDataId())
		tmpWheres = append(tmpWheres, "t.stat_time between ? and ?", startTime, endTime)
		if len(wheres) > 0 {
			tmpWheres = append(tmpWheres, wheres...)
		}
		err = this.DbSumsWithOpts(child, child,
			WithDbKeys(child.GetDbStatKeys()),
			WithDbWheres(wheres),
		)
		if err != nil {
			return nil, err
		}
	}
	if endTime > todayFirstSecond {
		todayStat = todayStat.GetTodayStat(todayStat)
		if err = child.Addin(todayStat); err != nil {
			return nil, err
		}
	}
	return child, nil
}

func (this *BaseStat) Persistent(child IStat) error {
	var (
		err  error
		list map[string]string
	)
	list, err = this.HGetAll(child)
	if err == nil {
		for _, v := range list {
			if err = this.BaseDb.FromX(child, v); err == nil {
				if this.StatTime != gbase.NewTime().DayFirstTime() { //日统计数据，只有新增 period type:day/month etc.

					err = this.Tx(child, func(tx *gorm.DB) error {
						//1.保存日统计数据
						if err = tx.Create(child).Error; err != nil {
							return err
						}

						//2.同时更新相关联的月，年，总统计
						totalModels := child.GetTotalModels()
						if len(totalModels) > 0 {
							for _, tm := range totalModels {
								isAdd := false
								if err = tm.DbGet(tm, "t.data_id", child.GetDataId()); err != nil {
									isAdd = true
								}

								if err = tm.Addin(child); err != nil {
									hlog.Errorf("非日统计持久化 %s failed,err:%s,v:%s", reflect.TypeOf(tm).Name(), err.Error(), v)
									return err
								}
								tm.SetStatTime(gbase.NewTime().DayFirstTime())
								if isAdd {
									tm.SetDataId(child.GetDataId())
									if err = tx.Create(tm).Error; err != nil {
										hlog.Errorf("非日统计持久化，新增数据失败,err:%s,child:%s", err.Error(), this.ToString(child))
										return err
									}
								} else {
									if err = tx.Updates(tm).Error; err != nil {
										hlog.Errorf("非日统计持久化，更新数据失败,err:%s,child:%s", err.Error(), this.ToString(child))
										return err
									}
								}
							}
						}

						//3.删除缓存日统计数据
						if err = this.RedisDel(child); err != nil {
							hlog.Errorf("stat persistent del redis data failed:%s,v:%s", err.Error(), v)
							return errors.New("del redis data failed")
						}
						return nil
					})
					if err != nil {
						if err = child.RedisSet(child); err != nil {
							hlog.Errorf("statShopDayPersistent 日统计持久化失败，重新存入统计数据到redis也失败：%s,丢失的redis数据:%s", err.Error(), v)
						}
						return err
					}

				}

			}
		}
	}
	return nil
}

//func WithStatToday2Db(today2Db bool) gbase.Option[IStat] {
//	return func(obj IStat) {
//		obj.SetToday2Db(today2Db)
//	}
//}
